package com.irain.pp.tool.mybatis.mgmt;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.irain.pp.tool.api.data.DataObject;
import com.irain.pp.tool.api.data.OtherCondition;
import com.irain.pp.tool.GenericsUtils;
import com.irain.pp.tool.annotation.NotNull;
import com.irain.pp.tool.mybatis.dto.Statistic;
import com.irain.pp.tool.mybatis.mapper.NativeSelect;
import com.irain.pp.tool.mybatis.mapper.PpBaseMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * @author WangYanPeng
 * @date 2017/11/18 15:55
 */
public abstract class BaseManagerImpl<DO extends DataObject> implements BaseManager<DO> {

    protected Class<DO> doClass = GenericsUtils.getSuperGenericClass(this.getClass(), BaseManagerImpl.class);

    private static final String SUM = "sum";

    private static final String COUNT = "count";

    @Autowired
    private PpBaseMapper<DO> mapper;

    protected static String notNullError(final String name) {
        return "'" + name + "' is must not be null.";
    }

    protected static String insertCountError(final int count) {
        return "It's insert " + count + " count.";
    }

    protected static String updateCountError(final int count) {
        return "It's update " + count + " count.";
    }

    protected static String deleteCountError(final int count) {
        return "It's deleteCacheData " + count + " cache.";
    }

    /*------------- select -----------*/

    @Override
    public DO selectById(Long id) {
        if (id == null) return null;
        return mapper.selectByPrimaryKey(id);
    }

    @NotNull
    @Override
    public List<DO> select(@NotNull DO condition) {
        Assert.notNull(condition, notNullError("condition"));
        return mapper.select(condition);
    }

    @NotNull
    @Override
    public List<DO> select(@NotNull DO condition, OtherCondition otherCondition) {
        return mapper.selectByExample(initExample(condition, otherCondition));
    }

    @NotNull
    @Override
    public Page<DO> select(@NotNull DO condition, OtherCondition otherCondition, int pageNum, int pageSize) {
        Page<DO> page = PageHelper.startPage(pageNum, pageSize);
        if (otherCondition == null) {
            select(condition);
        } else {
            select(condition, otherCondition);
        }
        return page;
    }

    @Override
    public int selectCount(@NotNull DO condition) {
        Assert.notNull(condition, notNullError("condition"));
        return mapper.selectCount(condition);
    }

    @Override
    public boolean exist(@NotNull DO condition) {
        PageHelper.startPage(1, 1);
        List<DO> list = select(condition);
        return list.size() == 1;
    }

    protected DO selectOne(@NotNull DO condition) {
        Assert.notNull(condition, notNullError("condition"));
        return mapper.selectOne(condition);
    }

    @NotNull
    protected List<DO> selectAll() {
        return mapper.selectAll();
    }

    public Statistic selectSumByExample(@NotNull String property, @NotNull Example example, boolean isCount) {
        return selectSumByExample(new String[]{property}, example, isCount);
    }

    public Statistic selectSumByExample(@NotNull String[] properties, @NotNull Example example, boolean isCount) {
        Assert.notNull(properties, notNullError("properties"));
        Assert.isTrue(properties.length > 0, notNullError("properties"));
        NativeSelect nativeSelect = new NativeSelect(doClass);
        for (int i = 0,len = properties.length; i < len ; i++) {
            nativeSelect.add(SUM + i, NativeSelect.SUM_ONE, properties[i]);
        }
        if (isCount) {
            nativeSelect.add(COUNT, NativeSelect.COUNT_ONE, DataObject.ID);
        }
        Map<String, Object> result = selectForNativeSqlByExample(nativeSelect, example);
        Statistic statistic = new Statistic();
        for (int i = 0,len = properties.length; i < len ; i++) {
            statistic.putSum(properties[i],new BigDecimal(result.get(SUM + i).toString()));
        }
        if (isCount) {
            statistic.setCount(Long.parseLong(result.get(COUNT).toString()));
        }
        return statistic;
    }

    protected Map<String, Object> selectForNativeSqlByExample(@NotNull NativeSelect nativeSelect, @NotNull Example example) {
        Assert.notNull(nativeSelect, notNullError("nativeSelect"));
        Assert.notNull(example, notNullError("example"));
        return mapper.selectForNativeSqlByExample(nativeSelect, example);
    }


    /*------------- other -------------*/

    @NotNull
    protected Example initExample() {
        return new Example(doClass);
    }

    @NotNull
    protected Example initExample(DO query) {
        return initExample(query, null);
    }

    @NotNull
    protected Example initExample(DO query, OtherCondition otherCondition) {
        Example example = new Example(doClass);
        Example.Criteria criteria = example.createCriteria();
        if (query != null) {
            criteria.andEqualTo(query);
        }
        if (otherCondition != null) {
            criteria.andGreaterThanOrEqualTo(DataObject.GMT_CREATED, otherCondition.getGmtCreatedBegin());
            criteria.andLessThan(DataObject.GMT_CREATED, otherCondition.getGmtCreatedEnd());
            criteria.andGreaterThanOrEqualTo(DataObject.GMT_MODIFIED, otherCondition.getGmtModifiedBegin());
            criteria.andLessThan(DataObject.GMT_MODIFIED, otherCondition.getGmtModifiedEnd());
            for (Map.Entry<String, Boolean> entry : otherCondition.getOrderByMap().entrySet()) {
                if (entry.getValue()) {
                    example.orderBy(entry.getKey());
                } else {
                    example.orderBy(entry.getKey()).desc();
                }
            }
        }
        return example;
    }
}
